using System;
using System.IO;
using System.Diagnostics;

using FridayShopping.Util;

namespace FridayShopping.DataModel
{
	/// <summary>
	/// Singleton. One class that through static members manages the whole 
	/// data of the program. It holds the current data read from the disk and 
	/// modified in memory. It stores and loads the data into and from xml files.
	/// </summary>
	public class Database
	{
		//private static readonly string   basePath = Configuration.Instance.DatabaseLocation;
		private static readonly string   basePath = Global.Settings.GetString(SettingKeys.DatabaseLocation);

		private	readonly string   productsFileName	= Path.Combine(basePath,"products.xml");
		private	readonly string   shopsFileName		= Path.Combine(basePath,"shops.xml");
		private	readonly string   categoriesFileName= Path.Combine(basePath,"categories.xml");
		public  readonly string   ExportCSVFile		= Path.Combine(basePath,"ShoppingList.CSV");
		public  readonly string   ShopListFile      = Path.Combine(basePath,Global.Settings.GetString(SettingKeys.PrintFile));

		private	static Database	instance;			
		
		/// <summary>
		/// Data tables
		/// </summary>
		private	Shops			shops;
		private Categories		categories;
		private Products		products;

		/// <summary>
		/// Private constructor. Creates an empty database
		/// </summary>
		private Database()
		{
			shops = new Shops();

			products = new Products();

			categories = new Categories();

		}

		static public Database Instance 
		{
			get
			{
				if (instance == null)
				{
					instance = new Database();
				}
				return instance;
			}
		}

		#region Save/Load data
		public void Save()
		{
			try 
			{
				shops.Save(shopsFileName);

				categories.Save(categoriesFileName);

				products.Save(productsFileName);
			}
			catch (IOException e) 
			{
				throw new DatabaseException("Failed to save database files " + e);
			}
		}

		public bool Exist
		{
			get
			{
				return (File.Exists(productsFileName) && File.Exists(shopsFileName) &&
					    File.Exists(categoriesFileName));
			}
		}

		public void Load()
		{
			if (!Directory.Exists(basePath)) 
			{
				Directory.CreateDirectory(basePath);
			}
			try 
			{
				shops = (Shops)Shops.Load(typeof(Shops), shopsFileName);

				categories = (Categories)Categories.Load(typeof(Categories),categoriesFileName);

				products = (Products)Products.Load(typeof(Products), productsFileName);
			}
			catch (Exception e) 
			{
				if (Exist) 
				{
					throw new DatabaseException("Failed to load database files " + e);
				}
			}

			AddItemsToCategories(products);
		}
		
		public void AddItemsToCategories(Products products) 
		{
			// Clear all categories
			foreach (Category category in categories.List)
			{
				category.Products = null;
			}
			//Establish item to category relationship
			foreach (Product item in products.List)
			{
				AddItemToCategory(item);
			}
		}

		#endregion

		#region Add/Delete Items from the category table
		public Key AddItem(Product item) 
		{			
			Key key = Products.Add(item);
			AddItemToCategory(item);
			return key;
		}

		public void DeleteItem(Product product)
		{
			Products.Delete(product.Key);
			Category category = (Category)categories.GetItem(product.CategoryKey);
			if (category != null) 
			{
				category.Products.Delete(product.Key);
			}
		}

		private void AddItemToCategory(Product product)
		{
			Category category = (Category)categories.GetItem(product.CategoryKey);
			if (category != null) 
			{
				category.Products.AddWithKey(product);
			}
		}
		#endregion

		#region Member Access
		public Shops Shops 
		{
			get
			{
				return shops;
			}
		}
		
		public Categories Categories
		{
			get
			{
				return categories;
			}
		}

		public Products Products 
		{
			get
			{
				return products;
			}
			set 
			{
				products = value;
			}
		}
		#endregion
	}


	/// <summary>
	/// Throw this exception when there is a problem with Saving/Loading database data
	/// </summary>
	public class DatabaseException : ApplicationException 
	{
		#region Constructors
		public DatabaseException() : base ()
		{
		}

		public DatabaseException(string msg) : base (msg) 
		{
		}

		public DatabaseException(string msg, Exception inner) : base (msg, inner)
		{
		}
		#endregion
	}
}
